package com.xuecheng.content.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.api.content.model.TeachplanDTO;
import com.xuecheng.api.content.model.dot.CourseBaseDTO;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.enums.common.CommonEnum;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.enums.content.TeachPlanEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.StringUtil;
import com.xuecheng.content.common.constant.ContentErrorCode;
import com.xuecheng.content.convert.CourseBaseConvert;
import com.xuecheng.content.convert.TeanchplanConvert;
import com.xuecheng.content.entity.CourseBase;
import com.xuecheng.content.entity.Teachplan;
import com.xuecheng.content.entity.TeachplanMedia;
import com.xuecheng.content.entity.ext.TeachplanNode;
import com.xuecheng.content.mapper.TeachplanMapper;
import com.xuecheng.content.service.CourseBaseService;
import com.xuecheng.content.service.TeachplanMediaService;
import com.xuecheng.content.service.TeachplanService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * @author by it-song
 * @Date 2021/12/11
 * @Description
 */
@Service
public class TeachplanServiceImpl extends ServiceImpl<TeachplanMapper, Teachplan> implements TeachplanService {

    @Autowired
    private CourseBaseService courseBaseService;

    @Autowired
    private TeachplanMediaService mediaService;

    /*
     * 1 查询不需要开启事务
     * 2 判断关键数据 courseID 和companyId
     * 3 判断业务数据  课程基础信息是否存在
     * 4 根据ID查询课程计划数据
     * 5 通过JAVA递归成课程计划树形结构
     * 6 将数据转为DTO并返回
     * */
    @Override
    public TeachplanDTO queryTreeNodesByCourseId(Long courseId, Long companyId) {
        //判断关键数据
        if (ObjectUtils.isEmpty(companyId) ||
                ObjectUtils.isEmpty(courseId)
        ) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        //查询课程信息
        LambdaQueryWrapper<CourseBase> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CourseBase::getId, courseId)
                .eq(CourseBase::getCompanyId, companyId);
        //
        CourseBase one = courseBaseService.getOne(queryWrapper);
        if (ObjectUtils.isEmpty(one)) {
            //判断是否为空
            ExceptionCast.cast(CommonErrorCode.E_110001);
        }
        //判断是不是使用态
        Integer status = one.getStatus();
        if (!(CommonEnum.USING_FLAG.getCodeInt().equals(status))) {
            ExceptionCast.cast(CommonErrorCode.E_100106);
        }

        //通过getMapper可以直接把dao层的方法注入到service层 在service层中调用该方法
        TeachplanMapper baseMapper = this.getBaseMapper();
        List<TeachplanNode> nodes = baseMapper.selectTreeNodesByCourseId(courseId);

        //如果nodes为空 就不要封装数据转换数据了
        TeachplanDTO dto = null;
        if (CollectionUtils.isEmpty(nodes)) {
            dto = new TeachplanDTO();
        } else {

            //通过JAVA生成课程树形结构
            //删除第几个返回删除的值 这里删除第一个 就返回第一个值
            TeachplanNode node = nodes.remove(0);

            generateTreeNodes(node, nodes);

            dto = TeanchplanConvert.INSTANCE.node2dto(node);
        }
        return dto;
    }

    private void generateTreeNodes(TeachplanNode node, List<TeachplanNode> nodes) {
        //判断父级集合是否为空  为空要创建集合
        if (CollectionUtils.isEmpty(node.getChildrenNodes())) {
            node.setChildrenNodes(new ArrayList<>(8));
        }

        //循环遍历集合生成树形结构
        for (TeachplanNode no : nodes) {
            //判断no是否和node有关联 如果有关联将关联的no存放到node集合中
            if (ObjectUtils.nullSafeEquals(node.getId(), no.getParentid())) {
                node.getChildrenNodes().add(no);
            }
            //如果是第三级课程计划 则无需递归  不是则递
            if (!(TeachPlanEnum.THIRD_LEVEL.equals(no.getGrade()))) {
                generateTreeNodes(no,nodes);
            }
        }

    }


    /*
     * 1主体方法
     *    1.1 判断业务操作
     *           是创建还是修改 teachplanId
     *                       有： 修改
     *                       没有：创建
     * 2 执行创建和修改操作并返回DTO数据
     * 3 返回结果数据DTO
     *
     * */
    //添加和修改要加事务
    @Transactional
    public TeachplanDTO createOrModifyTeachPlan(TeachplanDTO dto, Long companyId) {

        TeachplanDTO resutlDTO = null;

        //1.1判断业务操作
        if (ObjectUtils.isEmpty(dto)) {
            //添加操作
            resutlDTO = createTeachplan(dto, companyId);
        } else {
            //修改操作
            resutlDTO = modifyTeachplan(dto, companyId);
        }
        //返回数据结果
        return resutlDTO;
    }


    /*
     * 业务分析
     *  1 判断关键数据
     *  2 判断业务数据
     *           课程基本信息有没有
     *           判断是不是同一家机构
     *           判断是否删除
     *           判断课程基础信息审核状态 未提交和审核未通过才可以修改
     *  3 修改课程计划
     *  4 判断修改后的结果
     *  5  将数据库最新数据封装dto并返回
     *
     * */
    private TeachplanDTO modifyTeachplan(TeachplanDTO dto, Long companyId) {
        // *  1 判断关键数据
        if (ObjectUtils.isEmpty(companyId) ||
                StringUtil.isBlank(dto.getPname()) ||
                ObjectUtils.isEmpty(dto.getCourseId())
        ) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        // 2 判断业务数据
        LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Teachplan::getId, dto.getTeachPlanId());
        //因为这里只用于业务判断 所以直接返回查询数量即可
        int count = this.count(queryWrapper);
        if (count < 1) {
            ExceptionCast.cast(CommonErrorCode.E_100106);
        }

        //修改课程计划
        LambdaUpdateWrapper<Teachplan> updateWrapper = new LambdaUpdateWrapper<>();
        /*
         * 该修改内容只允许用户修改以下4点 通过ID来修改
         * 课程计划名称： pname
         * 开始直播时间： startTime
         * 直播结束时间： endTime
         * 是否支持试学或试看: is_preview
         * */
        updateWrapper.set(Teachplan::getPname, dto.getPname())
                .set(Teachplan::getStartTime, dto.getStartTime())
                .set(Teachplan::getEndTime, dto.getEndTime())
                .set(Teachplan::getIsPreview, dto.getIsPreview())
                .eq(Teachplan::getId, dto.getTeachPlanId());

        boolean b = this.update(updateWrapper);
        if (!b) {
            ExceptionCast.cast(CommonErrorCode.E_100106);
        }

        //获取最新数据
        Teachplan teachplan = this.getById(dto.getTeachPlanId());
        TeachplanDTO dto1 = TeanchplanConvert.INSTANCE.entity2dto(teachplan);
        return dto1;
    }

    /*
     * 业务分析
     *  1 判断关键数据
     *  2 判断业务数据
     *           课程基本信息有没有
     *           判断是不是同一家机构
     *           判断是否删除
     *           判断课程基础信息审核状态 未提交和审核未通过才可以修改
     *
     * 3 获得父级数据并进行关联
     *  parentid
     *   grade: 子级grade是父级grade+1 级别
     *   orderby 获得父级课程计划的子级数据个数+1
     * 4 将dto转为dto并保存
     * 5 判断保存后的结果
     * 6 将数据库最新数据转为dto并返回
     * */

    private TeachplanDTO createTeachplan(TeachplanDTO dto, Long companyId) {
        // *  1 判断关键数据
        if (ObjectUtils.isEmpty(companyId) ||
                StringUtil.isBlank(dto.getPname()) ||
                ObjectUtils.isEmpty(dto.getCourseId())
        ) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        //判断业务数据 用的是CuourseBasebyBaseId的判断方法 并返回数据
        CourseBase courseBase = courseBaseService.getCourseBaseByBaseId(dto.getTeachPlanId(), companyId);
        String auditStatus = courseBase.getAuditStatus();
        if (CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus) ||
                CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus) ||
                CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus)
        ) {
            ExceptionCast.cast(ContentErrorCode.E_120015);
        }
        /*
         *3 获得父级数据并进行关联
         *  parentid
         *   grade: 子级grade是父级grade+1 级别
         *   orderby 获得父级课程计划的子级数据个数+1
         * */

        //po转为dto
        CourseBaseDTO baseDTO = CourseBaseConvert.INSTANCE.entity2dto(courseBase);

        //dto转为teachplan
        Teachplan teachplan = TeanchplanConvert.INSTANCE.dto2entity(dto);

        Teachplan parentNode = generateParentNode(dto, baseDTO);

        teachplan.setParentid(parentNode.getParentid());
        teachplan.setGrade(parentNode.getGrade() + 1);

        LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Teachplan::getParentid, parentNode.getId());
        int count = this.count(queryWrapper);
        teachplan.setOrderby(count + 1);
        // 4.将dto转为po并保存
        boolean save = this.save(teachplan);
        // 5.判断保存后的结果
        if (!save) {
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }
        // 6.将数据库最新数据转为dto并返回
        Teachplan po = this.getById(teachplan.getId());
        TeachplanDTO dto1 = TeanchplanConvert.INSTANCE.entity2dto(po);
        return dto1;
    }


    private Teachplan generateParentNode(TeachplanDTO dto, CourseBaseDTO courseBase) {
        //判断新增目录是三级还是二级计划
        //判断新增的数据是否有parentId值
        // 有则是 三级课程计划
        // 无则是 二级课程计划
        Long parentid = dto.getParentid();
        if (ObjectUtils.isEmpty(parentid)) {
            //获得二级课程计划的父级数据
            //查询数据库获得一级课程计划 courseId, parentid=0
            //如果没有：自动创建并保存数据--数据来源 coursebase
            //如果有： 直接返回
            LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Teachplan::getCourseId, courseBase.getCourseBaseId());
            queryWrapper.eq(Teachplan::getParentid, TeachPlanEnum.FIRST_PARENTID_FLAG);

            Teachplan one = this.getOne(queryWrapper);
            if (ObjectUtils.isEmpty(one)) {
                one = new Teachplan();
                one.setPname(courseBase.getName());
                one.setParentid(new Long(TeachPlanEnum.FIRST_PARENTID_FLAG));
                one.setGrade(TeachPlanEnum.FIRST_LEVEL);
                one.setDescription(courseBase.getDescription());
                one.setOrderby(TeachPlanEnum.FIRST_LEVEL);
                one.setCourseId(courseBase.getCourseBaseId());
                boolean save = this.save(one);
                if (!save) {
                    ExceptionCast.cast(ContentErrorCode.E_120415);
                }
            }
            return one;
        } else {
            // 3.获得三级课程计划的父级数据
            //     查询数据库获得二级课程计划：parentId
            //         如果没有：直接抛出异常
            //         如果有：直接返回
            LambdaQueryWrapper<Teachplan> queryWrapper1 = new LambdaQueryWrapper<>();
            queryWrapper1.eq(Teachplan::getId, parentid);
            Teachplan one1 = this.getOne(queryWrapper1);
            if (ObjectUtils.isEmpty(one1)) {
                ExceptionCast.cast(ContentErrorCode.E_120408);
            }
            return one1;
        }
    }


    /*
     * 1 判断关键数据
     * 2 判断业务数据
     * 3 根据课程计划等级进行业务判断  是否有关联 有关联的要直接返回错误信息
     * 3 根据id删除课程计划
     *
     * */
    @Override
    public void removeTeachPlan(Long teachPlanId, Long companyId) {
        //判断关键数据的合法性
        Teachplan teachplan = getById(teachPlanId);
        if (ObjectUtils.isEmpty(teachplan)) {
            ExceptionCast.cast(ContentErrorCode.E_120402);
        }
        //判断课程基本信息是否存在
        CourseBase courseBase = courseBaseService.getById(teachplan.getCourseId());
        if (ObjectUtils.isEmpty(courseBase)) {
            ExceptionCast.cast(ContentErrorCode.E_120013);
        }
        //判断是否是同一家机构
        if (ObjectUtils.nullSafeEquals(courseBase.getCompanyId(), companyId)) {
            ExceptionCast.cast(CommonErrorCode.E_100108);
        }

        //根据课程计划等级进行业务判断(是否是二级目录)
        if (teachplan.getGrade()==TeachPlanEnum.SECEND_LEVEL) {

            // 判断二级课程计划是否有子级课程计划信息
            LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Teachplan::getParentid, teachPlanId);
            int count = this.count(queryWrapper);
            if (count > 0) {
                ExceptionCast.cast(ContentErrorCode.E_120409);
            } else {
                //判断第三级课程计划是否有关联的媒体资源
                LambdaQueryWrapper<TeachplanMedia> queryWrapper1 = new LambdaQueryWrapper<>();
                queryWrapper1.eq(TeachplanMedia::getTeachplanId, teachPlanId);
                int count1 = mediaService.count(queryWrapper1);
                if (count1 > 0) {
                    ExceptionCast.cast(ContentErrorCode.E_120413);
                }
                removeById(teachPlanId);
            }
        }
    }
}